#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>

#include <libconfig.h>


#include "asusosd_image.h"
#include "asusosd_queue.h"
#include <asm-generic/errno.h>
#include <errno.h>

struct config_t cfg;

static pthread_mutex_t	q_lock;
static pthread_cond_t	q_empty;

static int thread_alive;
char filename[128];
const char *temp;
char themepath[128] = "/usr/local/share/asus_osd/";
char fnf6command[128] = "/usr/bin/gnome-system-monitor &";
int xpos = 2;
int ypos = 2;

unsigned int	img_data;
int		wlan_value;
int		bri_value;
int		vol_value;
int		vol_level;
char		muted;
char		configfile[64];
config_setting_t *setting;

char* hotkey_get(int);
unsigned int hotkey_parse(char*);

void* image_thread(void*);

extern int create_image(char*);
extern void close_image();

void image_close();
void image_open();

int main (int argc, char *argv[])
{
	queue		*img_queue;
	pthread_t	img_thread;
	int		socket_fd;
	struct sockaddr_un socket_addr;
	char		*hotkey;
	unsigned int	data;
	FILE		*pfile;
	int		i;
	char		cmd[64];

	i = 1;
	while(i < argc)
	{
		if(!strcmp(argv[i], "-v"))
		{
			printf(VERSION"\n");
			exit(0);
		}
		i++;
	}

	/* Initialize the configuration */
  	config_init(&cfg);

  	/* Trying to load users conf-file */
  	sprintf(configfile, "%s/.asusosd/asusosd.conf", getenv("HOME"));
  	printf("Trying to read configfile %s...", configfile);
	if (!config_read_file(&cfg, configfile)) {
    		printf("failed\n");

		/* Trying to load generic conf-file */
		sprintf(configfile, "/etc/asusosd.conf");
	  	printf("Trying to read configfile %s...", configfile);
		if (!config_read_file(&cfg, configfile)) {
	    		printf("failed\n");
		} else {
			printf("success\n");
		}
	} else {
		printf("success\n");
	}

	setting = config_lookup(&cfg, "themepath");
    	if (setting) {
      		temp = config_setting_get_string(setting);
		strcpy(themepath, temp);
	}
	setting = config_lookup(&cfg, "fnf6command");
    	if (setting) {
		temp = config_setting_get_string(setting);
		sprintf(fnf6command,"%s &", temp);
	}
	setting = config_lookup(&cfg, "xpos");
    	if (setting) {
		xpos = config_setting_get_int(setting);
	}
	setting = config_lookup(&cfg, "ypos");
    	if (setting) {
		ypos = config_setting_get_int(setting);
	}


	img_queue = (queue *)queue_new(10);
	pthread_mutex_init(&q_lock, NULL);
	pthread_cond_init(&q_empty, NULL);
	pthread_create(&img_thread, NULL, image_thread, img_queue);

	socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
	if (socket_fd < 0) {
		printf("cannot create socket\n");
		return socket_fd;
	}

	memset(&socket_addr, 0, sizeof(socket_addr));
	socket_addr.sun_family = AF_UNIX;
	sprintf(socket_addr.sun_path, "%s", "/var/run/acpid.socket");

	if(connect(socket_fd, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) < 0)
	{
		printf("cannot connect socket\n");
		close(socket_fd);
		return -1;
	}

	fcntl(socket_fd, F_SETFD, FD_CLOEXEC);
	setvbuf(stdout, NULL, _IOLBF, 0);

	bri_value = 0;

	if((pfile=fopen("/proc/acpi/asus/brn","rb"))==NULL)
	{
		printf("cannot open /proc/acpi/asus/brn\n");
	}
	else
	{
		if(fscanf(pfile, "%d", &bri_value) != 1)
		{
			printf("cannot read /proc/acpi/asus/brn\n");
		}
		if(bri_value < 0 || 15 < bri_value)
		{
			bri_value = 0;
			printf("read an invalid value [%d] from /proc/acpi/asus/brn\n", bri_value);
		}
		fclose(pfile);
	}

	sprintf(cmd, "/etc/acpi/queryvolume.sh");
	vol_level = system(cmd) / 1024;

	while(1)
	{
		hotkey = hotkey_get(socket_fd);
		if(hotkey)
		{
			if(strncmp("hotkey ATKD ", hotkey, 12))
				continue;
			data = hotkey_parse(hotkey+12);
			if(data == IMG_TASK_MANAGER)
				continue;
			if(data > 0)
			{
				pthread_mutex_lock(&q_lock);
				while(img_queue->add(img_queue, (unsigned int)data))
				{
					pthread_mutex_unlock(&q_lock);
					pthread_cond_signal(&q_empty);
					usleep(1);
					pthread_mutex_lock(&q_lock);
				}
				pthread_mutex_unlock(&q_lock);
				pthread_cond_signal(&q_empty);
				usleep(2);
			}
		}
	}

	thread_alive = 0;
	pthread_cond_signal(&q_empty);
	pthread_join(img_thread, NULL);
	pthread_mutex_destroy(&q_lock);
	pthread_cond_destroy(&q_empty);
	queue_destroy(img_queue);
	close(socket_fd);
	return 0;
}

char*
hotkey_get(int sfd)
{
	static char *buf;
	int buflen = 64;
	int i = 0;
	int r;
	int searching = 1;

	while (searching) {
		buf = realloc(buf, buflen);
		if (!buf) {
			fprintf(stderr, "ERR: malloc(%d): %s\n",
				buflen, strerror(errno));
			return NULL;
		}
		memset(buf+i, 0, buflen-i);

		while (i < buflen) {
			r = read(sfd, buf+i, 1);
			if (r < 0 && errno != EINTR) {
				/* we should do something with the data */
				fprintf(stderr, "ERR: read(): %s\n",
					strerror(errno));
				return NULL;
			} else if (r == 0) {
				/* signal this in an almost standard way */
				errno = EPIPE;
				return NULL;
			} else if (r == 1) {
				/* scan for a newline */
				if (buf[i] == '\n') {
					searching = 0;
					buf[i] = '\0';
					break;
				}
				i++;
			}
		}
		if (buflen >= 1024/* max length of buffer */) {
			break;
		}
		buflen *= 2;
	}

	return buf;

}

unsigned int
hotkey_parse(char* data)
{
	int	hotkey;
	int	val;
	char		cmd[64];

	if(strncmp("000000", data, 6))
		return 0;
	if(data[6] == '2')
	{
		if('0'<=data[7] && data[7]<='9')
			val = data[7] - '0';
		else if('a'<=data[7] && data[7]<='f')
			val = data[7] -'a' +10;
		else if('A'<= data[7] && data[7]<='F')
			val = data[7] -'A' +10;
		else	val = bri_value;

		if((val > bri_value) || val==15)
			hotkey = IMG_BRN_UP|(val<<8);
		else if(val < bri_value || val==0)
			hotkey = IMG_BRN_DN|(val<<8);
		bri_value = val;
	}
	else
	{
		data[8] = 0;
		hotkey = atoi(data);
		switch(hotkey)
		{
			case 12: //taskmanager
				system(fnf6command);
				break;
			case 13: //mute
				if(muted) //off
				{
					muted = 0;
					sprintf(cmd, "amixer sset Front %d > /dev/null", (vol_level * 4));
					system(cmd);
					hotkey = 16;//then on
				}
				else
				{
					muted = 1;
					sprintf(cmd, "amixer sset Front 0 > /dev/null");
					system(cmd);
				}
				break;
			case 14:	//volume down
				if(muted) {
					muted = 0;
				}
				vol_level--;
				if(vol_level < 0)
				{
					vol_level = 0;
				}
				else if(vol_level > 16)
				{
					vol_level = 16;
				}
				sprintf(cmd, "amixer sset Front %i > /dev/null", (vol_level * 4));
				system(cmd);
				hotkey = hotkey|((vol_level) << 8);
				break;
			case 15:	//volume up
				if(muted) {
					muted = 0;
				}
				vol_level++;
				if(vol_level < 0)
				{
					vol_level = 0;
				}
				else if(vol_level > 16)
				{
					vol_level = 16;
				}
				sprintf(cmd, "amixer sset Front %i > /dev/null", (vol_level * 4));
				system(cmd);
				hotkey = hotkey|((vol_level) << 8);
				break;
		}
	}

	return (unsigned int)hotkey;
}

//static gboolean expose(GtkWidget *widget, GdkScreen *old_screen, gpointer userdata)
static int expose()
{
	unsigned int val;

	val = (img_data&VALUE_MASK) >> 8;
	memset(filename, 0, 128);

	if ((img_data&IMAGE_MASK) == IMG_WLAN_ON)
		sprintf(filename, "%s%s", themepath,"wlanon.png");
	else if ((img_data&IMAGE_MASK) == IMG_WLAN_OFF)
		sprintf(filename, "%s%s", themepath,"wlanoff.png");
	else if ((img_data&IMAGE_MASK) == IMG_DISP_LCD)
		sprintf(filename, "%s%s", themepath,"lcd.png");
	else if ((img_data&IMAGE_MASK) == IMG_DISP_CRT)
		sprintf(filename, "%s%s", themepath,"crt.png");
	else if ((img_data&IMAGE_MASK) == IMG_DISP_LCD_CRT)
		sprintf(filename, "%s%s", themepath,"crt_lcd.png");
	else if ((img_data&IMAGE_MASK) == IMG_AUDIO_MUTE)
		sprintf(filename, "%s%s", themepath,"VolumeMute.png");
	else if ((img_data&IMAGE_MASK) == IMG_AUDIO_ON)
		sprintf(filename, "%s%s", themepath,"VolumeOn.png");
	else if ((img_data&IMAGE_MASK) == IMG_BRN_DN)
		sprintf(filename, "%s%s%d%s", themepath,"BrightnessDown",val,".png");
	else if ((img_data&IMAGE_MASK) == IMG_BRN_UP)
		sprintf(filename, "%s%s%d%s", themepath,"BrightnessUP",val,".png");
	else if ((img_data&IMAGE_MASK) == IMG_VOL_DN)
		sprintf(filename, "%s%s%d%s", themepath,"VolumeDN",val,".png");
	else if ((img_data&IMAGE_MASK) == IMG_VOL_UP)
		sprintf(filename, "%s%s%d%s", themepath,"VolumeUP",val,".png");
	else if ((img_data&IMAGE_MASK) == IMG_AC_IN)
		sprintf(filename, "%s%s", themepath,"ACMode.png");
	else if ((img_data&IMAGE_MASK) == IMG_AC_OUT)
		sprintf(filename, "%s%s", themepath,"BatteryMode.png");

	else return -1;

	return 0;
}

void* image_thread (void *arg)
{
	queue	*q;
	int    ret;
	struct timespec tm;

	q = (queue *)arg;
	thread_alive = 1;

	while(thread_alive)
	{
		pthread_mutex_lock(&q_lock);

		while(q->isEmpty(q) && thread_alive)
		{
			if(tm.tv_sec > 0)
			{
				ret = pthread_cond_timedwait(&q_empty, &q_lock, &tm);
				if(ret == ETIMEDOUT)
					image_close();
				tm.tv_sec = 0;
			}
			else	pthread_cond_wait (&q_empty, &q_lock);
		}

		img_data = q->remove(q);
		pthread_mutex_unlock(&q_lock);

		image_open();
		tm.tv_sec = time(NULL) + 2;
	}

	return NULL;
}

void image_open()
{
	if(!expose())
	{
		create_image(filename);
	}
	else	printf("image_open error.\n");
}

void image_close()
{
	close_image();
}
